home *** CD-ROM | disk | FTP | other *** search
/ Amiga Tools 2 / Amiga Tools 2.iso / tools / vim / src / quickfix.c < prev    next >
C/C++ Source or Header  |  1995-03-09  |  9KB  |  437 lines

  1. /* vi:ts=4:sw=4
  2.  *
  3.  * VIM - Vi IMproved        by Bram Moolenaar
  4.  *
  5.  * Read the file "credits.txt" for a list of people who contributed.
  6.  * Read the file "uganda.txt" for copying and usage conditions.
  7.  */
  8.  
  9. /*
  10.  * quickfix.c: functions for quickfix mode, using a file with error messages
  11.  */
  12.  
  13. #include "vim.h"
  14. #include "globals.h"
  15. #include "proto.h"
  16. #include "param.h"
  17.  
  18. static void qf_free __ARGS((void));
  19. static char_u *qf_types __ARGS((int, int));
  20.  
  21. /*
  22.  * for each error the next struct is allocated and linked in a list
  23.  */
  24. struct qf_line
  25. {
  26.     struct qf_line    *qf_next;    /* pointer to next error in the list */
  27.     struct qf_line    *qf_prev;    /* pointer to previous error in the list */
  28.     linenr_t         qf_lnum;    /* line number where the error occurred */
  29.     int                 qf_fnum;    /* file number for the line */
  30.     int                 qf_col;    /* column where the error occurred */
  31.     int                 qf_nr;        /* error number */
  32.     char_u            *qf_text;    /* description of the error */
  33.     char_u             qf_cleared;/* set to TRUE if line has been deleted */
  34.     char_u             qf_type;    /* type of the error (mostly 'E') */
  35.     char_u             qf_valid;    /* valid error message detected */
  36. };
  37.  
  38. static struct qf_line *qf_start;        /* pointer to the first error */
  39. static struct qf_line *qf_ptr;            /* pointer to the current error */
  40.  
  41. static int    qf_count = 0;        /* number of errors (0 means no error list) */
  42. static int    qf_index;            /* current index in the error list */
  43. static int    qf_nonevalid;        /* set to TRUE if not a single valid entry found */
  44.  
  45. /*
  46.  * Read the errorfile into memory, line by line, building the error list.
  47.  * Return FAIL for error, OK for success.
  48.  */
  49.     int
  50. qf_init()
  51. {
  52.     char_u             namebuf[CMDBUFFSIZE + 1];
  53.     char_u            errmsg[CMDBUFFSIZE + 1];
  54.     int                col;
  55.     int                type;
  56.     int                valid;
  57.     long            lnum;
  58.     int                enr;
  59.     FILE            *fd;
  60.     struct qf_line    *qfp = NULL;
  61.     struct qf_line    *qfprev = NULL;        /* init to make SASC shut up */
  62.     char_u            *pfmt, *fmtstr;
  63. #ifdef UTS2
  64.     char_u            *(adr[7]);
  65. #else
  66.     void            *(adr[7]);
  67. #endif
  68.     int                adr_cnt = 0;
  69.     int                maxlen;
  70.     int                i;
  71.  
  72.     if (p_ef == NULL || *p_ef == NUL)
  73.     {
  74.         emsg(e_errorf);
  75.         return FAIL;
  76.     }
  77.     if ((fd = fopen((char *)p_ef, "r")) == NULL)
  78.     {
  79.         emsg2(e_openerrf, p_ef);
  80.         return FAIL;
  81.     }
  82.     qf_free();
  83.     qf_index = 0;
  84.     for (i = 0; i < 7; ++i)
  85.         adr[i] = NULL;
  86.  
  87. /*
  88.  * The format string is copied and modified from p_efm to fmtstr.
  89.  * Only a few % characters are allowed.
  90.  */
  91.         /* get some space to modify the format string into */
  92.         /* must be able to do the largest expansion 7 times (7 x 3) */
  93.     maxlen = STRLEN(p_efm) + 25;
  94.     fmtstr = alloc(maxlen);
  95.     if (fmtstr == NULL)
  96.         goto error2;
  97.     for (pfmt = p_efm, i = 0; *pfmt; ++pfmt, ++i)
  98.     {
  99.         if (pfmt[0] != '%')                /* copy normal character */
  100.             fmtstr[i] = pfmt[0];
  101.         else
  102.         {
  103.             fmtstr[i++] = '%';
  104.             switch (pfmt[1])
  105.             {
  106.             case 'f':        /* filename */
  107.                     adr[adr_cnt++] = namebuf;
  108.  
  109.             case 'm':        /* message */
  110.                     if (pfmt[1] == 'm')
  111.                         adr[adr_cnt++] = errmsg;
  112.                     fmtstr[i++] = '[';
  113.                     fmtstr[i++] = '^';
  114.                     if (pfmt[2])
  115.                         fmtstr[i++] = pfmt[2];
  116.                     else
  117. #ifdef MSDOS
  118.                         fmtstr[i++] = '\r';
  119. #else
  120.                         fmtstr[i++] = '\n';
  121. #endif
  122.                     fmtstr[i] = ']';
  123.                     break;
  124.             case 'c':        /* column */
  125.                     adr[adr_cnt++] = &col;
  126.                     fmtstr[i] = 'd';
  127.                     break;
  128.             case 'l':        /* line */
  129.                     adr[adr_cnt++] = &lnum;
  130.                     fmtstr[i++] = 'l';
  131.                     fmtstr[i] = 'd';
  132.                     break;
  133.             case 'n':        /* error number */
  134.                     adr[adr_cnt++] = &enr;
  135.                     fmtstr[i] = 'd';
  136.                     break;
  137.             case 't':        /* error type */
  138.                     adr[adr_cnt++] = &type;
  139.                     fmtstr[i] = 'c';
  140.                     break;
  141.             case '%':        /* %% */
  142.             case '*':        /* %*: no assignment */
  143.                     fmtstr[i] = pfmt[1];
  144.                     break;
  145.             default:
  146.                     EMSG("invalid % in format string");
  147.                     goto error2;
  148.             }
  149.             if (adr_cnt == 7)
  150.             {
  151.                 EMSG("too many % in format string");
  152.                 goto error2;
  153.             }
  154.             ++pfmt;
  155.         }
  156.         if (i >= maxlen - 6)
  157.         {
  158.             EMSG("invalid format string");
  159.             goto error2;
  160.         }
  161.     }
  162.     fmtstr[i] = NUL;
  163.  
  164.     while (fgets((char *)IObuff, CMDBUFFSIZE, fd) != NULL && !got_int)
  165.     {
  166.         if ((qfp = (struct qf_line *)alloc((unsigned)sizeof(struct qf_line))) == NULL)
  167.             goto error2;
  168.  
  169.         IObuff[CMDBUFFSIZE] = NUL;    /* for very long lines */
  170.         namebuf[0] = NUL;
  171.         errmsg[0] = NUL;
  172.         lnum = 0;
  173.         col = 0;
  174.         enr = -1;
  175.         type = 0;
  176.         valid = TRUE;
  177.  
  178.         if (sscanf((char *)IObuff, (char *)fmtstr, adr[0], adr[1], adr[2], adr[3],
  179.                                                 adr[4], adr[5]) != adr_cnt)
  180.         {
  181.             namebuf[0] = NUL;            /* something failed, remove file name */
  182.             valid = FALSE;
  183.             STRCPY(errmsg, IObuff);        /* copy whole line to error message */
  184.             if ((pfmt = STRRCHR(errmsg, '\n')) != NULL)
  185.                 *pfmt = NUL;
  186. #ifdef MSDOS
  187.             if ((pfmt = STRRCHR(errmsg, '\r')) != NULL)
  188.                 *pfmt = NUL;
  189. #endif
  190.         }
  191.  
  192.         if (namebuf[0] == NUL)            /* no file name */
  193.             qfp->qf_fnum = 0;
  194.         else
  195.             qfp->qf_fnum = buflist_add(namebuf);
  196.         if ((qfp->qf_text = strsave(errmsg)) == NULL)
  197.             goto error1;
  198.         qfp->qf_lnum = lnum;
  199.         qfp->qf_col = col;
  200.         qfp->qf_nr = enr;
  201.         qfp->qf_type = type;
  202.         qfp->qf_valid = valid;
  203.  
  204.         if (qf_count == 0)        /* first element in the list */
  205.         {
  206.             qf_start = qfp;
  207.             qfp->qf_prev = qfp;    /* first element points to itself */
  208.         }
  209.         else
  210.         {
  211.             qfp->qf_prev = qfprev;
  212.             qfprev->qf_next = qfp;
  213.         }
  214.         qfp->qf_next = qfp;        /* last element points to itself */
  215.         qfp->qf_cleared = FALSE;
  216.         qfprev = qfp;
  217.         ++qf_count;
  218.         if (qf_index == 0 && qfp->qf_valid)        /* first valid entry */
  219.         {
  220.             qf_index = qf_count;
  221.             qf_ptr = qfp;
  222.         }
  223.         breakcheck();
  224.     }
  225.     free(fmtstr);
  226.     if (!ferror(fd))
  227.     {
  228.         if (qf_index == 0)        /* no valid entry found */
  229.         {
  230.             qf_ptr = qf_start;
  231.             qf_index = 1;
  232.             qf_nonevalid = TRUE;
  233.         }
  234.         else
  235.             qf_nonevalid = FALSE;
  236.         fclose(fd);
  237.         qf_jump(0, 0);            /* display first error */
  238.         return OK;
  239.     }
  240.     emsg(e_readerrf);
  241. error1:
  242.     free(qfp);
  243. error2:
  244.     fclose(fd);
  245.     qf_free();
  246.     return FAIL;
  247. }
  248.  
  249. /*
  250.  * jump to a quickfix line
  251.  * if dir == FORWARD go "errornr" valid entries forward
  252.  * if dir == BACKWARD go "errornr" valid entries backward
  253.  * else if "errornr" is zero, redisplay the same line
  254.  * else go to entry "errornr"
  255.  */
  256.     void
  257. qf_jump(dir, errornr)
  258.     int        dir;
  259.     int        errornr;
  260. {
  261.     linenr_t        i;
  262.  
  263.     if (qf_count == 0)
  264.     {
  265.         emsg(e_quickfix);
  266.         return;
  267.     }
  268.  
  269.     if (dir == FORWARD)        /* next valid entry */
  270.     {
  271.         while (errornr--)
  272.         {
  273.             do
  274.             {
  275.                 if (qf_index == qf_count || qf_ptr->qf_next == NULL)
  276.                     break;
  277.                 ++qf_index;
  278.                 qf_ptr = qf_ptr->qf_next;
  279.             } while (!qf_nonevalid && !qf_ptr->qf_valid);
  280.         }
  281.     }
  282.     else if (dir == BACKWARD)        /* previous valid entry */
  283.     {
  284.         while (errornr--)
  285.         {
  286.             do
  287.             {
  288.                 if (qf_index == 1 || qf_ptr->qf_prev == NULL)
  289.                     break;
  290.                 --qf_index;
  291.                 qf_ptr = qf_ptr->qf_prev;
  292.             } while (!qf_nonevalid && !qf_ptr->qf_valid);
  293.         }
  294.     }
  295.     else if (errornr != 0)        /* go to specified number */
  296.     {
  297.         while (errornr < qf_index && qf_index > 1 && qf_ptr->qf_prev != NULL)
  298.         {
  299.             --qf_index;
  300.             qf_ptr = qf_ptr->qf_prev;
  301.         }
  302.         while (errornr > qf_index && qf_index < qf_count && qf_ptr->qf_next != NULL)
  303.         {
  304.             ++qf_index;
  305.             qf_ptr = qf_ptr->qf_next;
  306.         }
  307.     }
  308.  
  309.     /*
  310.      * If there is a file name, 
  311.      * read the wanted file if needed, and check autowrite etc.
  312.      */
  313.     if (qf_ptr->qf_fnum == 0 || buflist_getfile(qf_ptr->qf_fnum, (linenr_t)1, TRUE) == OK)
  314.     {
  315.         /*
  316.          * Go to line with error, unless qf_lnum is 0.
  317.          */
  318.         i = qf_ptr->qf_lnum;
  319.         if (i > 0)
  320.         {
  321.             if (i > curbuf->b_ml.ml_line_count)
  322.                 i = curbuf->b_ml.ml_line_count;
  323.             curwin->w_cursor.lnum = i;
  324.         }
  325.         curwin->w_cursor.col = qf_ptr->qf_col;
  326.         adjust_cursor();
  327.         cursupdate();
  328.         smsg((char_u *)"(%d of %d) %s%s: %s", qf_index, qf_count, 
  329.                     qf_ptr->qf_cleared ? (char_u *)"(line deleted) " : (char_u *)"",
  330.                     qf_types(qf_ptr->qf_type, qf_ptr->qf_nr), qf_ptr->qf_text);
  331.     }
  332. }
  333.  
  334. /*
  335.  * list all errors
  336.  */
  337.     void
  338. qf_list()
  339. {
  340.     struct qf_line *qfp;
  341.     int i;
  342.  
  343.     if (qf_count == 0)
  344.     {
  345.         emsg(e_quickfix);
  346.         return;
  347.     }
  348.     qfp = qf_start;
  349.     gotocmdline(TRUE, NUL);
  350.     for (i = 1; !got_int && i <= qf_count; ++i)
  351.     {
  352.         sprintf((char *)IObuff, "%2d line %3ld col %2d %s: %s",
  353.             i,
  354.             (long)qfp->qf_lnum,
  355.             qfp->qf_col,
  356.             qf_types(qfp->qf_type, qfp->qf_nr),
  357.             qfp->qf_text);
  358.         msg_outstr(IObuff);
  359.         msg_outchar('\n');
  360.         qfp = qfp->qf_next;
  361.         flushbuf();                    /* show one line at a time */
  362.         breakcheck();
  363.     }
  364.     wait_return(FALSE);
  365. }
  366.  
  367. /*
  368.  * free the error list
  369.  */
  370.     static void
  371. qf_free()
  372. {
  373.     struct qf_line *qfp;
  374.  
  375.     while (qf_count)
  376.     {
  377.         qfp = qf_start->qf_next;
  378.         free(qf_start->qf_text);
  379.         free(qf_start);
  380.         qf_start = qfp;
  381.         --qf_count;
  382.     }
  383. }
  384.  
  385. /*
  386.  * qf_mark_adjust: adjust marks
  387.  */
  388.    void
  389. qf_mark_adjust(line1, line2, inc)
  390.     linenr_t    line1;
  391.     linenr_t    line2;
  392.     long        inc;
  393. {
  394.     register int i;
  395.     struct qf_line *qfp;
  396.  
  397.     if (qf_count)
  398.         for (i = 0, qfp = qf_start; i < qf_count; ++i, qfp = qfp->qf_next)
  399.             if (qfp->qf_fnum == curbuf->b_fnum &&
  400.                             qfp->qf_lnum >= line1 && qfp->qf_lnum <= line2)
  401.             {
  402.                 if (inc == MAXLNUM)
  403.                     qfp->qf_cleared = TRUE;
  404.                 else
  405.                     qfp->qf_lnum += inc;
  406.             }
  407. }
  408.  
  409. /*
  410.  * Make a nice message out of the error character and the error number:
  411.  *    char    number        message
  412.  *  e or E    0            "  Error"
  413.  *  w or W    0            "Warning"
  414.  *  other     0             ""
  415.  *  w or W    n            "Warning n"
  416.  *  other     n            "  Error n"
  417.  */
  418.     static char_u *
  419. qf_types(c, nr)
  420.     int c, nr;
  421. {
  422.     static char_u    buf[20];
  423.     char_u        *p1;
  424.  
  425.     p1 = (char_u *)"  Error";
  426.     if (c == 'W' || c == 'w')
  427.         p1 =  (char_u *)"Warning";
  428.     else if (nr <= 0 && c != 'E' && c != 'e')
  429.         p1 = (char_u *)"";
  430.  
  431.     if (nr <= 0)
  432.         return p1;
  433.  
  434.     sprintf((char *)buf, "%s %3d", p1, nr);
  435.     return buf;
  436. }
  437.